package aqs;

import java.util.concurrent.locks.AbstractQueuedSynchronizer;

/**
 * 模仿jdk的ReentrantLock，用AQS实现自己的ReentrantLock
 * 2021.4.16: 已完成非公平的 lock() 和 unlock()
 * @author Jay
 */
public class MyReentrantLock {
    /**
     *  同步器
     *  根据构造方法参数的不同创建公平和非公平两种同步器
     *  目前仅提供非公平同步器
     */
    private final Sync sync;

    public MyReentrantLock(){
        this(false);
    }

    public MyReentrantLock(boolean fair) {
        this.sync = new NonFairSync();
    }

    /**
     * 上锁，可重入
     */
    public void lock(){
        sync.lock();
    }

    /**
     * 尝试加锁，竞争锁失败不会阻塞
     * @return 竞争是否成功
     */
    public boolean tryLock(){
        return sync.nonFairTryAcquire(1);
    }

    /**
     * 释放锁
     */
    public void unlock(){
        sync.release(1);
    }

    /**
     * 同步器抽象
     * 1、提供方法抽象，由NonFairSync和FairSync实现
     * 2、提供默认方法（公平和非公平场景下相同）：nonFairTryAcquire，tryRelease
     */
    abstract static class Sync extends AbstractQueuedSynchronizer{
        /**
         * 加锁
         */
        abstract void lock();

        /**
         * 竞争锁过程
         * 1、尝试cas state获得锁
         * 2、判断是否是重入
         * @param acquires arg
         * @return 竞争状态
         */
        final boolean nonFairTryAcquire(int acquires){
            int state = getState();
            Thread currentThread = Thread.currentThread();
            // 再次尝试修改state（获得锁）（因为有可能运行到这里时锁已经被释放了，不能默认认为得不到锁）
            if(state == 0){
                if(compareAndSetState(0, 1)){
                    setExclusiveOwnerThread(currentThread);
                    return true;
                }
            }
            // 锁被持有，但是可能是同一线程，即可重入
            else{
                // 判断是否是重入
                if(currentThread == getExclusiveOwnerThread()){
                    int updateState = state + acquires;
                    if(updateState < 0){ // overflow
                        throw new Error("超出最大重入次数");
                    }
                    setState(updateState);
                    return true;
                }
            }
            // 再次尝试和重入判断都失败，则视为竞争失败
            return false;
        }

        /**
         * 尝试释放锁，完全释放时返回true，非完全释放（即有重入）返回false
         * @param releases arg
         * @return 释放状态
         */
        @Override
        protected boolean tryRelease(int releases) {
            int newState = getState() - releases;
            // unlock调用者不是锁持有者
            if(Thread.currentThread() != getExclusiveOwnerThread()){
                throw new IllegalMonitorStateException();
            }
            boolean free = false;
            /*
                state ！= 0 代表此时锁还处于重入状态，所以不能将owner线程置空，也不能返回锁空闲
             */
            if(newState == 0){
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(newState);
            return free;
        }
    }

    /**
     * 非公平同步器
     */
    static class NonFairSync extends Sync{
        @Override
        protected boolean tryAcquire(int acquires) {
            return nonFairTryAcquire(acquires);
        }

        @Override
        void lock() {
            // 锁空闲，可以直接获得
            if(compareAndSetState(0, 1)){
                setExclusiveOwnerThread(Thread.currentThread());
            }
            // 锁已被持有，可能是竞争，可能是重入
            else{
                /*
                if (!tryAcquire(arg) &&
                    acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
                    selfInterrupt();
                进入acquire如上所示，首先会使用tryAcquire再次竞争锁（该方法中带有重入的判断）
                如果竞争还是失败，将调用acquireQueued将线程加入阻塞队列
                 */
                acquire(1);
            }
        }
    }
}
